iT邦幫忙

2024 iThome 鐵人賽

DAY 29
0
JavaScript

從PM到前端開發:我的React作品集之旅系列 第 29

Day 29:表單數據持久化 — 優化用戶體驗

  • 分享至 

  • xImage
  •  

在我們的前幾篇文章中,我們已經介紹了如何使用表單來捕獲用戶輸入數據,並通過 EmailJS 發送表單數據。今天,我們將深入探討如何利用 sessionStorage 來實現數據持久化,並結合加密技術來保護用戶的敏感信息。我們會比較不同的數據持久化選項,並且著重於如何加密數據以增強安全性。

數據持久化技術比較

在前端開發中,有多種選擇來存儲數據,常見的包括 localStoragesessionStoragecookies。我們在這裡比較它們的特點:

技術 有效範圍 持久性 容量 安全性
localStorage 整個應用程序 永久存儲,直到被刪除 約 5-10 MB 沒有自動加密,需手動加密
sessionStorage 當前會話 在會話結束後清除 約 5-10 MB 沒有自動加密,需手動加密
Cookies 整個應用或特定域 可設定過期時間 約 4 KB 可以設置 HttpOnly 和 Secure 標誌

我們選擇 sessionStorage 的原因

  1. 有效性sessionStorage 適合存儲在當前會話期間需要保留的數據,且在用戶關閉瀏覽器後會自動清除。
  2. 容量:儘管容量限制比 localStorage 小,但對於表單這樣的應用來說已經足夠。
  3. 加密保護:我們將結合 CryptoJS 對數據進行加密,從而保護敏感數據。

實際演練

接下來,我們將以 "Let’s Collaborate" 表單為例,展示如何使用 sessionStorage 進行加密和數據持久化。

Step 1: 安裝所需的包

首先,安裝 CryptoJS 用於加密和解密數據:

 npm install crypto-js 

Step 2: 定義加密密鑰

為了保護數據,我們將使用一個加密密鑰來加密表單數據。在你的環境變數文件(.env)中,添加如下密鑰:

REACT_APP_SECRET_KEY=your_secret_key

Step 3: 初始化表單狀態

接下來,我們先初始化表單數據,並填入到useForm。這裡我們將使用 sessionStorage 來持久化表單數據,確保它在當前瀏覽器會話中保存。

import React, { useState } from 'react';
import { useForm } from 'react-hook-form';
import * as styles from '@/components/Form/ContactForm.module.scss';
import CryptoJS from 'crypto-js';

const ContactForm = () => {
    // 初始化表單數據狀態
    const [formData, setFormData] = useState({
        name: '',
        email: '',
        serviceItem: '',
        projectDetails: ''
    });

    // 使用 useForm hook 來管理表單
    const { register, handleSubmit, formState: { errors }, reset } = useForm({
        defaultValues: formData  // 使用初始狀態來填充表單
    });

    return (
        <form className={styles.contactForm} onSubmit={handleSubmit(onSubmit)}>
            {/* 其他表單項目 */}
        </form>
    );
};

說明:

  • formData:使用 useState 初始化表單數據。
  • useForm:React Hook Form 的 useForm 用於管理表單的輸入和狀態。

Step 3: 讀取加密數據

在頁面加載時,我們需要從 sessionStorage 中檢查是否已經保存了加密的表單數據,並將其解密後填充到表單中。

import { useEffect } from 'react';

// 從環境變數中讀取加密密鑰
const SECRET_KEY = process.env.REACT_APP_SECRET_KEY;

const ContactForm = () => {
    // Step 2: 從 sessionStorage 加載加密數據
    useEffect(() => {
        const savedData = sessionStorage.getItem('contactForm');
        if (savedData) {
            const bytes = CryptoJS.AES.decrypt(savedData, SECRET_KEY);
            const decryptedData = JSON.parse(bytes.toString(CryptoJS.enc.Utf8));
            setFormData(decryptedData);
            reset(decryptedData);  // 重設表單數據
        }
    }, [reset]);

    return (
        <form className={styles.contactForm} onSubmit={handleSubmit(onSubmit)}>
            {/* 其他表單項目 */}
        </form>
    );
};

Step 4: 表單變更的保存

每次當用戶在表單中輸入或修改數據時,我們將這些數據加密並保存到 sessionStorage 中,以便在會話期間持續保存數據。

useEffect(() => {
    const encryptedData = CryptoJS.AES.encrypt(JSON.stringify(formData), SECRET_KEY).toString();
    sessionStorage.setItem('contactForm', encryptedData);
}, [formData]);

const handleInputChange = (e) => {
    const { name, value } = e.target;
    setFormData((prevData) => ({
        ...prevData,
        [name]: value,
    }));
};

return (
   <form className={styles.contactForm} onSubmit={handleSubmit(onSubmit)}>
            <div className={styles.formGroup}>
                <label htmlFor="name">Name</label>
                <input
                    type="text"
                    {...register('name', { required: 'Name is required' })}
                    placeholder="Your Name"
                    value={formData.name}  // 綁定到狀態
                    onChange={handleInputChange}  // 監聽變更
                />
                {errors.name && <p className={styles.errorText}>{errors.name.message}</p>}
            </div>
            <div className={styles.formGroup}>
                <label htmlFor="email">Email</label>
                <input
                    type="email"
                    {...register('email', {
                        required: 'Email is required',
                        pattern: {
                            value: /\S+@\S+\.\S+/,
                            message: 'Enter a valid email',
                        },
                    })}
                    placeholder="Your Email"
                    value={formData.email}  // 綁定到狀態
                    onChange={handleInputChange}  // 監聽變更
                />
                {errors.email && <p className={styles.errorText}>{errors.email.message}</p>}
            </div>
            <div className={styles.formGroup}>
                <label htmlFor="serviceItem">Service Items</label>
                <select
                    {...register('serviceItem', { required: 'Please select a service item' })}
                    value={formData.serviceItem}  // 綁定到狀態
                    onChange={handleInputChange}  // 監聽變更
                >
                    <option value="">Select a service</option>
                    <option value="design">Design</option>
                    <option value="development">Development</option>
                    <option value="consultation">Consultation</option>
                </select>
                {errors.serviceItem && <p className={styles.errorText}>{errors.serviceItem.message}</p>}
            </div>
            <div className={styles.formGroup}>
                <label htmlFor="projectDetails">Project Details</label>
                <textarea
                    {...register('projectDetails', { required: 'Project details are required' })}
                    placeholder="Describe your project"
                    value={formData.projectDetails}  // 綁定到狀態
                    onChange={handleInputChange}  // 監聽變更
                ></textarea>
                {errors.projectDetails && <p className={styles.errorText}>{errors.projectDetails.message}</p>}
            </div>
        {/* 其他表單項目 */}
    </form>
);

Step 5: 提交表單並清除 sessionStorage

在表單提交後,我們會清除 sessionStorage 中的表單數據,避免重複填充舊數據。

const onSubmit = (data) => {
        setIsSending(true); // 開始發送,設置 loading 狀態

        // EmailJS 發送邏輯
        const templateParams = {
            from_name: data.name,
            from_email: data.email,
            service: data.serviceItem,
            message: data.projectDetails,
        };

        emailjs
            .send(
                process.env.EMAILJS_SERVICE_ID,  // 使用環境變數中的 Service ID
                process.env.EMAILJS_TEMPLATE_ID,  // 使用環境變數中的 Template ID
                templateParams,
                process.env.EMAILJS_PUBLIC_KEY  // 使用環境變數中的 Public Key
            )
            .then(
                (response) => {
                    console.log('Email sent successfully!', response.status, response.text);
                    alert('Your message was sent successfully!');
                    sessionStorage.removeItem('contactForm');  // 提交後清除 sessionStorage
                    setIsSending(false); // 發送完成,重置 loading 狀態
                },
                (error) => {
                    console.log('Failed to send email...', error);
                    alert('There was an error sending your message.');
                    setIsSending(false); // 發送失敗,重置 loading 狀態
                }
            );
    };

結語

在這篇文章中,我們學習了如何使用 sessionStorage 來持久化表單數據,並使用加密技術來保護用戶數據的安全性。sessionStorage 適合短期的數據存儲,當瀏覽器關閉後自動清除數據,這使其成為表單和購物車等短期使用數據的理想選擇。


流光館Luma<∕> ✨ 期待與你繼續探索更多技術知識!



上一篇
Day 28: 無需後端,利用 EmailJS 輕鬆處理表單郵件
下一篇
Day 30: 總結與 Vercel 部署實戰
系列文
從PM到前端開發:我的React作品集之旅30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言